<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>双模 WebSocket 客户端</title>
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            max-width: 1000px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f5f7fa;
            color: #333;
        }

        header {
            text-align: center;
            margin-bottom: 20px;
            padding-bottom: 15px;
            border-bottom: 1px solid #e0e6ed;
        }

        h1 {
            color: #2c3e50;
            margin-bottom: 10px;
        }

        .container {
            display: flex;
            gap: 20px;
        }

        .panel {
            flex: 1;
            background: white;
            border-radius: 8px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            padding: 20px;
            min-height: 500px;
        }

        .mode-toggle {
            display: flex;
            justify-content: center;
            margin-bottom: 20px;
        }

        .mode-btn {
            padding: 10px 20px;
            margin: 0 5px;
            border: none;
            border-radius: 20px;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .mode-btn.active {
            background-color: #3498db;
            color: white;
            box-shadow: 0 2px 5px rgba(52, 152, 219, 0.5);
        }

        .mode-btn:not(.active):hover {
            background-color: #ecf0f1;
        }

        .mode-indicator {
            padding: 8px 15px;
            border-radius: 4px;
            font-weight: bold;
            text-align: center;
            margin-bottom: 15px;
        }

        .client-mode {
            background-color: #d4edda;
            color: #155724;
        }

        .server-mode {
            background-color: #cce5ff;
            color: #004085;
        }

        button {
            padding: 10px 15px;
            background-color: #3498db;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
            transition: background-color 0.2s;
        }

        button:hover {
            background-color: #2980b9;
        }

        button:disabled {
            background-color: #bdc3c7;
            cursor: not-allowed;
        }

        .status {
            padding: 10px;
            border-radius: 4px;
            margin: 10px 0;
            font-weight: bold;
        }

        .connected {
            background-color: #d4edda;
            color: #155724;
        }

        .disconnected {
            background-color: #f8d7da;
            color: #721c24;
        }

        .clients-list {
            border: 1px solid #ddd;
            border-radius: 4px;
            padding: 10px;
            max-height: 200px;
            overflow-y: auto;
            margin: 15px 0;
            background-color: #f8f9fa;
        }

        .client-item {
            padding: 8px;
            border-bottom: 1px solid #eee;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .client-item:last-child {
            border-bottom: none;
        }

        .client-actions {
            display: flex;
            gap: 5px;
        }

        .client-btn {
            padding: 4px 8px;
            font-size: 12px;
        }

        textarea, input {
            width: 100%;
            padding: 10px;
            box-sizing: border-box;
            border: 1px solid #ddd;
            border-radius: 4px;
            margin-bottom: 10px;
            font-family: inherit;
        }

        .message-container {
            height: 300px;
            overflow-y: auto;
            border: 1px solid #ddd;
            border-radius: 4px;
            padding: 10px;
            margin-top: 10px;
            background-color: #fafafa;
        }

        .message-item {
            margin-bottom: 8px;
            padding: 8px;
            border-bottom: 1px solid #eee;
            font-family: monospace;
            font-size: 14px;
            border-left: 3px solid #3498db;
            background-color: white;
        }

        .message-item.server {
            border-left-color: #2ecc71;
        }

        .message-item.client {
            border-left-color: #e74c3c;
        }

        .message-timestamp {
            color: #888;
            font-size: 12px;
            margin-right: 10px;
        }

        .message-source {
            font-weight: bold;
            color: #3498db;
        }

        .message-source.server {
            color: #2ecc71;
        }

        .message-source.client {
            color: #e74c3c;
        }

        .form-group {
            margin-bottom: 15px;
        }

        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
            color: #2c3e50;
        }
    </style>
</head>
<body>
<header>
    <h1>双模 WebSocket 客户端</h1>
    <p>支持客户端模式和服务器模式，满足不同连接需求</p>
</header>

<div class="mode-toggle">
    <button id="clientModeBtn" class="mode-btn active">客户端模式</button>
    <button id="serverModeBtn" class="mode-btn">服务器模式</button>
</div>

<div id="modeIndicator" class="mode-indicator client-mode">当前模式：客户端模式</div>

<div class="container">
    <!-- 客户端模式面板 -->
    <div id="clientPanel" class="panel">
        <h2>客户端设置</h2>

        <div class="form-group">
            <label for="serverUrl">服务器地址：</label>
            <input type="text" id="serverUrl" value="ws://localhost:8080/ws">
        </div>

        <div>
            <button id="connectBtn">建立连接</button>
            <button id="disconnectBtn" disabled>断开连接</button>
        </div>

        <div id="clientStatus" class="status disconnected">未连接</div>

        <div class="form-group">
            <label for="messageType">消息类型：</label>
            <select id="messageType">
                <option value="C_TO_S_AUTH_ACTIVE">认证激活</option>
                <option value="C_TO_S_CUSTOM_MSG">自定义消息</option>
            </select>
        </div>

        <div class="form-group">
            <label for="messageInput">消息内容：</label>
            <textarea id="messageInput" placeholder="输入JSON格式的消息内容"></textarea>
        </div>

        <button id="sendBtn" disabled>发送消息</button>
    </div>

    <!-- 服务器模式面板 -->
    <div id="serverPanel" class="panel" style="display: none;">
        <h2>服务器设置</h2>

        <div class="form-group">
            <label for="listenPort">监听端口：</label>
            <input type="number" id="listenPort" value="8081">
        </div>

        <div>
            <button id="startServerBtn">启动服务器</button>
            <button id="stopServerBtn" disabled>停止服务器</button>
        </div>

        <div id="serverStatus" class="status disconnected">服务器未启动</div>

        <h3>已连接客户端：</h3>
        <div id="clientsList" class="clients-list">
            <!-- 客户端列表将动态添加 -->
            <div class="empty-list">暂无连接客户端</div>
        </div>

        <div class="form-group">
            <label for="targetClient">目标客户端：</label>
            <select id="targetClient" disabled>
                <option value="all">所有客户端</option>
            </select>
        </div>

        <div class="form-group">
            <label for="serverMessageInput">消息内容：</label>
            <textarea id="serverMessageInput" placeholder="输入要发送的消息"></textarea>
        </div>

        <button id="sendToClientBtn" disabled>发送给客户端</button>
    </div>
</div>

<div class="panel">
    <h2>消息日志</h2>
    <div id="messages-container" class="message-container">
        <div id="messages"></div>
    </div>
</div>

<script>
    // WebSocket 相关变量
    let ws = null;
    let serverWs = null;
    let heartTimeout = null;
    let connectedClients = new Map(); // 存储连接的客户端 sessionId => WebSocket

    // DOM 元素
    const clientModeBtn = document.getElementById('clientModeBtn');
    const serverModeBtn = document.getElementById('serverModeBtn');
    const modeIndicator = document.getElementById('modeIndicator');
    const clientPanel = document.getElementById('clientPanel');
    const serverPanel = document.getElementById('serverPanel');
    const connectBtn = document.getElementById('connectBtn');
    const disconnectBtn = document.getElementById('disconnectBtn');
    const sendBtn = document.getElementById('sendBtn');
    const clientStatus = document.getElementById('clientStatus');
    const messageInput = document.getElementById('messageInput');
    const messagesDiv = document.getElementById('messages');
    const messageTypeSelect = document.getElementById('messageType');
    const startServerBtn = document.getElementById('startServerBtn');
    const stopServerBtn = document.getElementById('stopServerBtn');
    const serverStatus = document.getElementById('serverStatus');
    const clientsList = document.getElementById('clientsList');
    const targetClientSelect = document.getElementById('targetClient');
    const serverMessageInput = document.getElementById('serverMessageInput');
    const sendToClientBtn = document.getElementById('sendToClientBtn');
    const serverUrlInput = document.getElementById('serverUrl');
    const listenPortInput = document.getElementById('listenPort');

    // 模式切换
    clientModeBtn.addEventListener('click', () => {
        clientModeBtn.classList.add('active');
        serverModeBtn.classList.remove('active');
        modeIndicator.textContent = '当前模式：客户端模式';
        modeIndicator.className = 'mode-indicator client-mode';
        clientPanel.style.display = 'block';
        serverPanel.style.display = 'none';
    });

    serverModeBtn.addEventListener('click', () => {
        serverModeBtn.classList.add('active');
        clientModeBtn.classList.remove('active');
        modeIndicator.textContent = '当前模式：服务器模式';
        modeIndicator.className = 'mode-indicator server-mode';
        clientPanel.style.display = 'none';
        serverPanel.style.display = 'block';
    });

    // 客户端模式：建立连接
    connectBtn.addEventListener('click', () => {
        if (ws && ws.readyState !== WebSocket.CLOSED) {
            logMessage("连接已存在，无需重复创建");
            return;
        }

        const serverUrl = serverUrlInput.value;
        ws = new WebSocket(serverUrl);
        updateClientStatus("connecting", "连接中...");
        disableClientButtons(true, true, true);

        ws.onopen = () => {
            logMessage("连接服务器成功", 'server');
            updateClientStatus("connected", "已连接");
            disableClientButtons(true, false, false);

            // 自动发送认证消息
            const authMsg = {
                type: "C_TO_S_AUTH_ACTIVE",
                code: "saga32",
                ip: "192.168.0.10",
                mac: "38-1B-9E-BF-A7-0A"
            };
            ws.send(JSON.stringify(authMsg));
            logMessage(`已发送认证消息: ${JSON.stringify(authMsg)}`, 'client');

            // 启动心跳检测
            resetHeartbeat();
        };

        ws.onmessage = (event) => {
            try {
                const data = JSON.parse(event.data);
                logMessage(`收到消息: ${JSON.stringify(data)}`, 'server');
                scrollToBottom();

                // 处理心跳消息
                if (data.type === "S_TO_C_HEART") {
                    resetHeartbeat();
                }
            } catch (e) {
                logMessage(`收到非JSON消息: ${event.data}`, 'server');
            }
        };

        ws.onerror = (error) => {
            logMessage(`连接错误: ${error.message || "未知错误"}`, 'error');
            updateClientStatus("error", "连接错误");
            clearHeartbeat();
            disableClientButtons(false, true, true);
        };

        ws.onclose = () => {
            logMessage("连接已关闭", 'info');
            updateClientStatus("disconnected", "连接已断开");
            clearHeartbeat();
            disableClientButtons(false, true, true);
            scrollToBottom();
        };
    });

    // 客户端模式：断开连接
    disconnectBtn.addEventListener('click', () => {
        if (ws && ws.readyState === WebSocket.OPEN) {
            ws.close();
        }
    });

    // 客户端模式：发送消息
    sendBtn.addEventListener('click', () => {
        if (!ws || ws.readyState !== WebSocket.OPEN) {
            logMessage("连接未建立，无法发送消息", 'error');
            return;
        }

        try {
            const messageType = messageTypeSelect.value;
            let messageContent = {};

            if (messageType === "C_TO_S_CUSTOM_MSG") {
                messageContent = JSON.parse(messageInput.value || "{}");
            }

            const message = {
                type: messageType,
                ...messageContent
            };

            ws.send(JSON.stringify(message));
            logMessage(`已发送消息: ${JSON.stringify(message)}`, 'client');
            messageInput.value = "";
            scrollToBottom();
        } catch (e) {
            logMessage(`消息格式错误: ${e.message}`, 'error');
            scrollToBottom();
        }
    });

    // 服务器模式：启动服务器
    startServerBtn.addEventListener('click', () => {
        const port = listenPortInput.value || "8081";
        try {
            // 在实际环境中，这里应该使用WebSocket服务器库
            // 以下为模拟实现
            serverWs = new WebSocket(`ws://localhost:${port}`);

            // 模拟服务器监听
            updateServerStatus("connecting", "服务器启动中...");
            disableServerButtons(true, true, true);

            setTimeout(() => {
                updateServerStatus("connected", `服务器已启动，监听端口 ${port}`);
                disableServerButtons(true, false, false);
                logMessage(`WebSocket服务器已启动，监听端口 ${port}`, 'server');

                // 模拟有客户端连接
                setTimeout(() => {
                    const sessionId = `client_${Math.floor(Math.random() * 1000)}`;
                    const clientIp = `192.168.1.${Math.floor(Math.random() * 100)}`;

                    // 添加到客户端列表
                    addClientToList(sessionId, clientIp);

                    logMessage(`客户端 ${clientIp} 已连接 (ID: ${sessionId})`, 'server');
                }, 1500);

                // 模拟第二个客户端连接
                setTimeout(() => {
                    const sessionId = `client_${Math.floor(Math.random() * 1000)}`;
                    const clientIp = `192.168.1.${Math.floor(Math.random() * 100)}`;

                    // 添加到客户端列表
                    addClientToList(sessionId, clientIp);

                    logMessage(`客户端 ${clientIp} 已连接 (ID: ${sessionId})`, 'server');
                }, 3000);
            }, 1000);
        } catch (e) {
            logMessage(`服务器启动失败: ${e.message}`, 'error');
            updateServerStatus("error", "服务器启动失败");
            disableServerButtons(false, true, true);
        }
    });

    // 服务器模式：停止服务器
    stopServerBtn.addEventListener('click', () => {
        if (serverWs) {
            serverWs.close();
        }

        // 清除所有客户端连接
        connectedClients.clear();
        updateClientsList();

        updateServerStatus("disconnected", "服务器已停止");
        disableServerButtons(false, true, true);
        logMessage("WebSocket服务器已停止", 'server');
    });

    // 服务器模式：向客户端发送消息
    sendToClientBtn.addEventListener('click', () => {
        const message = serverMessageInput.value;
        if (!message.trim()) {
            logMessage("消息内容不能为空", 'error');
            return;
        }

        const target = targetClientSelect.value;
        if (target === 'all') {
            // 广播给所有客户端
            logMessage(`广播消息: ${message}`, 'server');
        } else {
            // 发送给特定客户端
            logMessage(`发送给客户端 ${target}: ${message}`, 'server');
        }

        serverMessageInput.value = "";
    });

    // 添加客户端到列表
    function addClientToList(sessionId, ip) {
        connectedClients.set(sessionId, {
            sessionId: sessionId,
            ip: ip,
            lastActive: new Date()
        });

        updateClientsList();
    }

    // 更新客户端列表
    function updateClientsList() {
        clientsList.innerHTML = '';

        if (connectedClients.size === 0) {
            clientsList.innerHTML = '<div class="empty-list">暂无连接客户端</div>';
            return;
        }

        connectedClients.forEach((client, sessionId) => {
            const clientItem = document.createElement('div');
            clientItem.className = 'client-item';

            clientItem.innerHTML = `
                    <div>
                        <strong>${client.ip}</strong>
                        <div><small>ID: ${sessionId}</small></div>
                    </div>
                    <div class="client-actions">
                        <button class="client-btn disconnect-btn" data-id="${sessionId}">断开</button>
                    </div>
                `;

            clientsList.appendChild(clientItem);
        });

        // 更新目标客户端选择器
        updateTargetClientSelector();

        // 添加断开连接事件
        document.querySelectorAll('.disconnect-btn').forEach(btn => {
            btn.addEventListener('click', (e) => {
                const sessionId = e.target.dataset.id;
                disconnectClient(sessionId);
            });
        });
    }

    // 更新目标客户端选择器
    function updateTargetClientSelector() {
        targetClientSelect.innerHTML = '<option value="all">所有客户端</option>';

        connectedClients.forEach((client, sessionId) => {
            const option = document.createElement('option');
            option.value = sessionId;
            option.textContent = `${client.ip} (${sessionId})`;
            targetClientSelect.appendChild(option);
        });

        targetClientSelect.disabled = connectedClients.size === 0;
        sendToClientBtn.disabled = connectedClients.size === 0;
    }

    // 断开客户端连接
    function disconnectClient(sessionId) {
        if (connectedClients.has(sessionId)) {
            const client = connectedClients.get(sessionId);
            connectedClients.delete(sessionId);

            logMessage(`已断开客户端 ${client.ip} (ID: ${sessionId})`, 'server');
            updateClientsList();
        }
    }

    // 心跳机制
    function resetHeartbeat() {
        clearTimeout(heartTimeout);
        // 35秒未收到心跳视为断开
        heartTimeout = setTimeout(() => {
            logMessage("服务器心跳丢失，断开连接", 'error');
            if (ws && ws.readyState === WebSocket.OPEN) {
                ws.close();
            }
        }, 35000);
    }

    function clearHeartbeat() {
        clearTimeout(heartTimeout);
    }

    // 更新客户端状态显示
    function updateClientStatus(status, text) {
        clientStatus.className = `status ${status}`;
        clientStatus.textContent = text;
    }

    // 更新服务器状态显示
    function updateServerStatus(status, text) {
        serverStatus.className = `status ${status}`;
        serverStatus.textContent = text;
    }

    // 客户端按钮状态控制
    function disableClientButtons(connectDisabled, disconnectDisabled, sendDisabled) {
        connectBtn.disabled = connectDisabled;
        disconnectBtn.disabled = disconnectDisabled;
        sendBtn.disabled = sendDisabled;
    }

    // 服务器按钮状态控制
    function disableServerButtons(startDisabled, stopDisabled, sendDisabled) {
        startServerBtn.disabled = startDisabled;
        stopServerBtn.disabled = stopDisabled;
        sendToClientBtn.disabled = sendDisabled;
    }

    // 日志记录
    function logMessage(message, source = 'info') {
        const messageElement = document.createElement('div');
        messageElement.className = `message-item ${source}`;

        const timestamp = new Date();
        const sourceText = source === 'client' ? '客户端' :
            source === 'server' ? '服务器' :
                source === 'error' ? '错误' : '信息';

        messageElement.innerHTML = `
                <span class="message-timestamp">[${timestamp.toLocaleTimeString()}]</span>
                <span class="message-source ${source}">[${sourceText}]</span>
                <span>${message}</span>
            `;

        messagesDiv.appendChild(messageElement);
        scrollToBottom();
    }

    // 滚动到底部
    function scrollToBottom() {
        const container = document.getElementById('messages-container');
        container.scrollTop = container.scrollHeight;
    }

    // 初始化
    function init() {
        logMessage("双模 WebSocket 客户端已启动，请选择模式进行操作", 'info');
        disableClientButtons(false, true, true);
        disableServerButtons(false, true, true);
    }

    // 页面加载完成后初始化
    window.addEventListener('DOMContentLoaded', init);
</script>
</body>
</html>